home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / BarChart / ChartOfMatrix.m < prev    next >
Text File  |  1993-03-30  |  18KB  |  642 lines

  1. /* 
  2.  * BarChart, A simple multi source loadable class.
  3.  *
  4.  *    Written by: Joe Freeman 7/92
  5.  *    
  6.  *    HSB color sweep stolen from some of Randy Nelson's code
  7.  *        should use NXEqualColor() for duplicates
  8.  *
  9.  */
  10.  
  11.  
  12. #import "ChartOfMatrix.h"
  13. #import <dpsclient/psops.h>
  14. #import <c.h>
  15. #import <stdio.h>
  16.  
  17. #define NUM_BOGUS    8    /* number of bars to draw when no data */
  18. #define COM_VERSION    3.3    /* version 3 ported to 3.0 */
  19.  
  20. @implementation ChartOfMatrix
  21.  
  22.  
  23. /*============================================================
  24.  *factory
  25.  *============================================================*/
  26.  
  27.  
  28. + initialize
  29. {
  30.     [super initialize];
  31.     [ChartOfMatrix setVersion:2];
  32.     return self;
  33. }
  34.  
  35. - (const char *)getInspectorClassName 
  36.     return "ChartOfMatrixInspector"; }
  37.  
  38. - initFrame:(NXRect *)r
  39. {
  40.     self = [super initFrame:r];
  41.     minSheetSet = 0.0;
  42.     maxSheetSet = 1.0;
  43.     COM_Flags.autoScale = YES;
  44.     COM_Flags.drawType = DRAW_V_BAR;
  45.     backgroundColor = NX_COLORWHITE;
  46.     highlightColor = NX_COLORBLACK;
  47.     highlightIndex = MAXINT;        /* don't show on default */
  48.     COM_Flags.drawFrame = YES;
  49.     hMargin = vMargin = 15.0;
  50.     hMargin = vMargin = 5.0;
  51.     numPrototypes = 5;
  52.     COM_Flags.randomBarColors = YES;
  53.     return self;
  54. }
  55.  
  56. - awake
  57. {
  58.     [super awake];
  59.     highlightIndex = MAXINT;
  60.     [self registerForDraggedTypes:&NXColorPboardType count:1];
  61.     return self;
  62. }
  63.  
  64. /*============================================================
  65.  * color dragging support
  66.  *============================================================*/
  67.  
  68. - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
  69. {
  70.     if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
  71.     return NX_DragOperationGeneric;
  72.     } else {
  73.     return NX_DragOperationNone;
  74.         }
  75.     }
  76.  
  77. - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
  78. {
  79.     NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
  80.     [self setBackgroundColor: c];
  81.     return YES;
  82. }
  83. /*============================================================
  84.  * instance set / query methods
  85.  *============================================================*/
  86.  
  87. - setDataSrc:anObject
  88. {
  89.     dataSrc = anObject;
  90.     return self;
  91. }
  92.  
  93. - setGraphType:(int)drawCode
  94.     { COM_Flags.drawType = drawCode;  [self update]; return self; }
  95. - (int)graphType
  96.     { return COM_Flags.drawType; }
  97.  
  98. - takeRandomColorStateFrom:sender
  99.     { [self setRandomBarColorEnabled:[sender state]]; return self; }
  100. - setRandomBarColorEnabled:(BOOL)flag
  101.     { COM_Flags.randomBarColors = flag; [self update]; return self; }
  102. - (BOOL)isRandomBarColorEnabled
  103.     { return COM_Flags.randomBarColors; }
  104.     
  105. - takeBackgroundColorFrom:sender
  106.     { [self setBackgroundColor:  [sender color]]; return self; }
  107. - setBackgroundColor:(NXColor)aColor
  108.     {  backgroundColor = aColor; [self update]; return self; }
  109. - (NXColor)backgroundColor
  110.     { return backgroundColor; }
  111.     
  112. - takeHighlightColorFrom:sender
  113.     { [self setHighlightColor:  [sender color]]; return self; }
  114. - setHighlightColor:(NXColor)aColor
  115.     {  highlightColor = aColor; [self update]; return self; }
  116. - (NXColor)highlightColor
  117.     { return highlightColor; }
  118.  
  119.  
  120. /* set and query the size for the margins (in points) */
  121. - takeHMarginFrom:sender
  122.     { hMargin = [sender floatValue]; [self update]; return self; }
  123. - takeVMarginFrom:sender
  124.     { vMargin = [sender floatValue]; [self update]; return self; }
  125. - (float)hMargin
  126.     { return hMargin; }
  127. - (float)vMargin
  128.     { return vMargin; }
  129.  
  130. - takeAutoScaleStateFrom:sender
  131.     { COM_Flags.autoScale = [sender floatValue]; return self; }
  132. - setAutoScale:(BOOL)flag
  133.     { COM_Flags.autoScale = flag; [self update]; return self; }
  134. - (BOOL)autoScale
  135.     { return COM_Flags.autoScale; }
  136.  
  137. /* fancy controls */
  138. - takeFrameStateFrom:sender
  139.     { COM_Flags.drawFrame = [sender state]; [self update]; return self; }
  140. - (BOOL)frameState
  141.     { return COM_Flags.drawFrame; }
  142.  
  143. - takeNumProtosFrom:sender
  144.     { numPrototypes= [sender intValue]; [self update]; return self; }
  145. - (int)numProtos
  146.     { return numPrototypes; }
  147.  
  148. - takeBorderTypeFrom:sender
  149.     { borderType = [sender tag]; [self update]; return self; }
  150. - (int)borderType
  151.     { return borderType; }
  152.  
  153. /* when autoscale is off, set the min and max for the sheet */
  154. - takeMinValueFrom:sender
  155.     { minSheetSet = [sender floatValue]; return self; }
  156. - takeMaxValueFrom:sender
  157.     { maxSheetSet = [sender floatValue]; return self; }
  158. - (double)minValue
  159.     { return minSheetSet; }
  160. - (double)maxValue
  161.     { return maxSheetSet; }
  162.  
  163.   
  164. /*============================================================
  165.  * target/action
  166.  *============================================================*/
  167.  
  168. - (BOOL)acceptsFirstResponder { return YES; }
  169.  
  170. - copy:sender
  171. {
  172.     id pb = [Pasteboard new];    /* global pasteboard object */
  173.     NXStream    *st;        /* stream to collect data in */
  174.     char        *data;        /* actual data buffer */
  175.     int        length;        /* length of data */
  176.     int        maxLength;    /* (not used here) */
  177.     
  178.     /* declare that we will supply  a single type of data, eps */
  179.     [pb declareTypes:&NXPostScriptPboard num:1 owner:self];
  180.     
  181.     /* get a stream which writes to memory */
  182.     st = NXOpenMemory (NULL, 0, NX_WRITEONLY);
  183.     
  184.     /* find bounding box and then write it to the stream */
  185.     [self copyPSCodeInside:&bounds to:st];
  186.     
  187.     /* get actuall data buffer form stream */
  188.     NXGetMemoryBuffer (st, &data, &length, &maxLength);
  189.     
  190.     /* write data to pasteboard */
  191.     [pb writeType:NXPostScriptPboard data:data length:length ];
  192.     
  193.     /* dealocate stream including it's buffer */
  194.     NXCloseMemory (st, NX_FREEBUFFER );
  195.     
  196.  
  197.     return self;
  198. }
  199.  
  200. /*============================================================
  201.  * do real work
  202.  *============================================================*/
  203.  
  204. - (int)numLocations
  205. {
  206.     int numRows,numCols;
  207.  
  208.     if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
  209.         return [dataSrc count];
  210.     } else if ([dataSrc respondsTo:@selector(selectedCell)]){
  211.         [dataSrc getNumRows:&numRows numCols:&numCols];
  212.     return MAX(numRows,numCols);
  213.     } else if (!dataSrc) {
  214.         /* put up some dummy graph on palette */
  215.     return NUM_BOGUS;
  216.     }
  217.     return 0;
  218. }
  219.  
  220. - (float)valueOfLocation:(int)n
  221. {
  222.     static     float    data[NUM_BOGUS] = {.1, .3, .2, .7, .4, .8, .5, .98};
  223.     int numRows,numCols;
  224.     float theValue;
  225.     id      mrValue = [[DBValue alloc] init];
  226.  
  227.     if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
  228.        [dataSrc getValue:mrValue forProperty:mrExpression at:n];
  229.     theValue = [mrValue floatValue];
  230.     [mrValue free];
  231.     return theValue;
  232.     } else if ([dataSrc respondsTo:@selector(selectedCell)]){
  233.         [dataSrc getNumRows:&numRows numCols:&numCols];
  234.     if (numCols == 1) {
  235.         return [[dataSrc cellAt:n :0] floatValue];
  236.     } else {     /* numRows == 1 */
  237.         return [[dataSrc cellAt:0 :n] floatValue];
  238.     }
  239.     } else if (!dataSrc) {
  240.         return data[n];
  241.     }
  242.     
  243.     return 0.0;
  244. }
  245.  
  246. /*============================================================
  247.  * target action loading
  248.  *============================================================*/
  249.  
  250. - plotFromMatrix:sender
  251. {
  252.     if (!dataSrc && [sender respondsTo:@selector(selectedCell)]){
  253.         [self setDataSrc:sender];
  254.     } 
  255.     [self update];
  256.     return self;
  257. }
  258.  
  259. /*============================================================
  260.  * dbKit support
  261.  *============================================================*/
  262.  
  263. - associationContentsDidChange:association
  264. {
  265.     mrFetchGroup =     [association fetchGroup];
  266.     dataSrc =         [mrFetchGroup recordList];
  267.     highlightIndex =     [mrFetchGroup currentRecord];
  268.     mrExpression =     [association expression];
  269.     [self update];
  270.     return self;
  271. }
  272.  
  273. /*
  274. ** adh 7/27/92
  275. ** Do this so we redraw when values are updated in the UI
  276. */
  277. - association:association setValue:(DBValue *)value
  278. {
  279.     return [self update];
  280. }
  281.  
  282. - associationSelectionDidChange:association
  283. {
  284.     /* assume fetchgroup doesn't change so don't update mrFetchGroup */
  285.     highlightIndex =  [[association fetchGroup] currentRecord];
  286.     return [self update];
  287. }
  288.  
  289. - associationCurrentRecordDidDelete:association
  290. {
  291.     /* assumes record list is the same */
  292.     return [self update];
  293.     return self;
  294. }
  295.  
  296. /*============================================================
  297.  * do a selection with the mouse
  298.  *============================================================*/
  299.  
  300. - mouseDown:(NXEvent *)theEvent
  301. {
  302.     NXEvent     lastEvent;
  303.     NXRect     rectOfBar;
  304.     NXRect    drawRect;
  305.     int     longSize = [self numLocations];
  306.     int        index;
  307.  
  308.     
  309.     if ((COM_Flags.drawType != DRAW_H_BAR && 
  310.                 COM_Flags.drawType != DRAW_V_BAR ) ||
  311.         ![dataSrc respondsTo:@selector(getValue:forProperty:at:)]) {
  312.         NXBeep();
  313.     return nil;
  314.     }
  315.     
  316.     lastEvent = *theEvent;
  317.     [self convertPoint: &lastEvent.location fromView:nil];
  318.     /* modify the mouse position to be in psuedo drawing area coords */
  319.     lastEvent.location.x -= hMargin;
  320.     lastEvent.location.y -= vMargin;
  321.  
  322.     /* allow for the margins when calc'ing the bar moused down on */
  323.     drawRect = bounds;
  324.     drawRect.origin.x +=     hMargin;
  325.     drawRect.origin.y +=     vMargin;
  326.     drawRect.size.width -=     (2*hMargin);
  327.     drawRect.size.height -=     (2*vMargin);
  328.     
  329.     for ( index = 0 ; index < longSize; index++){
  330.         if (COM_Flags.drawType == DRAW_V_BAR) {
  331.         [self calcRect:&rectOfBar ofBar:index
  332.             insideRect:&drawRect
  333.             usingMin:NX_Y(&bounds) ];
  334.         rectOfBar.origin.y = bounds.origin.y;
  335.         rectOfBar.size.height = bounds.size.height;
  336.     } else  if (COM_Flags.drawType == DRAW_H_BAR) {
  337.         [self calcRect:&rectOfBar ofBar:index
  338.             insideRect:&drawRect
  339.             usingMin:NX_X(&bounds) ];
  340.         rectOfBar.origin.x = bounds.origin.x;
  341.         rectOfBar.size.width = bounds.size.width;
  342.     } else {
  343.         return nil;
  344.     }
  345.     if ([self mouse:&lastEvent.location inRect:&rectOfBar]){
  346.         [mrFetchGroup setCurrentRecord:index];
  347.         break;
  348.     }
  349.     }
  350.     return self;
  351.  
  352. /*============================================================
  353.  * target/action
  354.  *============================================================*/
  355.  
  356. - read:(NXTypedStream *)stream
  357. {
  358.         int tmpScale, tmpDrawFrame, tmpDrawType;
  359.     [super read:stream];
  360.     dataSrc =  NXReadObject(stream);
  361.     minField = NXReadObject(stream);
  362.     maxField =  NXReadObject(stream);
  363.     meanField = NXReadObject(stream);
  364.     /* the first demo palette went out as version 1 */
  365.     if (NXTypedStreamClassVersion(stream, "ChartOfMatrix")<2) {
  366.         NXReadTypes(stream,"ffffiii",
  367.                     &minSheetSet,&maxSheetSet,
  368.                     &vMargin,&hMargin,
  369.                     &tmpScale,
  370.                     &tmpDrawType,
  371.                     &tmpDrawFrame);
  372.         COM_Flags.autoScale = tmpScale;
  373.         COM_Flags.drawType = tmpDrawType;
  374.         COM_Flags.drawFrame = tmpDrawFrame;
  375.         COM_Flags.randomBarColors = YES;
  376.     } else {
  377.         NXReadTypes(stream,"ffffi",
  378.                     &minSheetSet,&maxSheetSet,
  379.                     &vMargin,&hMargin,
  380.                     &COM_Flags);
  381.     }
  382.     
  383.     backgroundColor = NXReadColor(stream);
  384.     highlightColor = NXReadColor(stream);
  385.     NXReadTypes(stream,"ii",
  386.                     &numPrototypes,
  387.                     &borderType);
  388.     return self;
  389. }
  390.  
  391. - write:(NXTypedStream *)stream
  392. {
  393.     [super write:stream];
  394.     NXWriteObjectReference(stream, dataSrc);
  395.     NXWriteObjectReference(stream, minField);
  396.     NXWriteObjectReference(stream, maxField);
  397.     NXWriteObjectReference(stream, meanField);
  398.     NXWriteTypes(stream,"ffffi",
  399.                     &minSheetSet,&maxSheetSet,
  400.                     &vMargin,&hMargin,
  401.                     &COM_Flags);
  402.     NXWriteColor(stream,        backgroundColor);
  403.     NXWriteColor(stream,        highlightColor);
  404.     NXWriteTypes(stream,"ii",
  405.                     &numPrototypes,
  406.                     &borderType);
  407.     return self;
  408. }
  409.  
  410. /*============================================================
  411.  *display
  412.  *============================================================*/
  413.  
  414. - calcMin:(float *)rMin andMax:(float *)rMax andMean:(float *)rMean;
  415. {
  416.     int    index;
  417.     int    longSize;
  418.     float    thisVal;
  419.     float    sumAll = 0;
  420.  
  421.     *rMin = *rMax = [self valueOfLocation:0];
  422.     longSize = [self numLocations];
  423.  
  424.     /* first figure out what the maxs and mins are */
  425.     for ( index = 0 ; index < longSize; index++){
  426.         thisVal = [self valueOfLocation:index];
  427.         if (*rMin > thisVal)        *rMin = thisVal;
  428.         if (*rMax < thisVal)        *rMax = thisVal;
  429.         sumAll += thisVal;
  430.     }
  431.     *rMean = sumAll / longSize;
  432.     
  433.     [minField setFloatValue:*rMin];
  434.     [maxField setFloatValue:*rMax];
  435.     [meanField setFloatValue:*rMean];
  436.     return self;
  437. }
  438.  
  439. - renderVLines:(NXRect *)r  min:(float )minSheetVal max:(float )maxSheetVal
  440. {
  441.     int    longSize;    /* numRows or numCols, whichever is the long side */
  442.     int    index;
  443.     float    thisVal;
  444.     float    cellWidth;    /* the width of a unit (N) to plot */
  445.  
  446.     /* scale the plot */
  447.     PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
  448.     PStranslate(0.0, -minSheetVal);
  449.         
  450.     longSize = [self numLocations];
  451.     cellWidth = r->size.width / (longSize);
  452.  
  453.     /* now plot each square */
  454.     PSsetgray(NX_BLACK);
  455.     thisVal = [self valueOfLocation:0];
  456.     PSmoveto(cellWidth/2.0, thisVal);
  457.     for ( index = 0 ; index < longSize; index++){
  458.         thisVal = [self valueOfLocation:index];
  459.         PSlineto(cellWidth*index+ cellWidth/2.0, thisVal);
  460.     }
  461.     PSstroke();
  462.     return self;
  463. }
  464.  
  465. /* we can position the bars anywhere inside the view by changing the
  466.  * insideRect parameter 
  467.  */
  468. - calcRect:(NXRect *)r 
  469.     ofBar:(int)n 
  470.     insideRect:(NXRect *)boundingRect
  471.     usingMin:(float)minSheetVal 
  472. {
  473.     float    thisVal;
  474.     float    cellWidth;    /* width of a unit (N) to plot */
  475.  
  476.     thisVal = [self valueOfLocation:n];
  477.     if (COM_Flags.drawType == DRAW_V_BAR){
  478.     cellWidth = boundingRect->size.width / (3 * [self numLocations] + 1);
  479.     r->origin.x = n * 3 * cellWidth + cellWidth;
  480.     if (thisVal < 0.0){
  481.                 r->origin.y = thisVal;
  482.                 r->size.height = -thisVal;
  483.     }else{
  484.                 r->origin.y = MAX(minSheetVal, 0.0);
  485.                 r->size.height= thisVal-r->origin.y;
  486.     }
  487.     r->size.width = 2 * cellWidth;
  488.     } else /* assume h bar */ {
  489.     cellWidth = boundingRect->size.height / (3 * [self numLocations] + 1);
  490.     r->origin.y = n * 3 * cellWidth + cellWidth;
  491.     if (thisVal < 0.0){
  492.                 r->origin.x = thisVal;
  493.                 r->size.width = -thisVal;
  494.     }else{
  495.                 r->origin.x = MAX(minSheetVal, 0.0);
  496.                 r->size.width= thisVal - r->origin.x;
  497.     }
  498.     r->size.height = 2 * cellWidth;
  499.     }
  500.  
  501.     return self;
  502. }
  503.  
  504. /* this should probably be empty but because Kris asked for a bar/lines switch 
  505.  * in the inspectorwe have the abillity to draw bars (vertical).  Of course  
  506.  * this makes the vertical bar drawing object almost codeless
  507.  */
  508. - renderBars:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal
  509. {
  510.     int    longSize;    /* numRows/numCols, whichever is long side */
  511.     int    index;
  512.     NXRect    barRect;    /* bounding rectangle for bar in the chart */
  513.     NXColor    HSBColor;    /* in case randomBarColors */
  514.  
  515.     longSize = [self numLocations];
  516.     if (COM_Flags.drawType == DRAW_V_BAR){
  517.         /* scale the plot */
  518.         PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
  519.         PStranslate(0.0, -minSheetVal);
  520.     } else /* assume h bar */ {
  521.         /* scale the plot */
  522.         PSscale(r->size.width / ( maxSheetVal - minSheetVal), 1.0 );
  523.         PStranslate( -minSheetVal, 0.0);
  524.     }
  525.         
  526.     /* now plot each square */
  527.     for ( index = 0 ; index < longSize; index++){
  528.         if (index == highlightIndex)
  529.             NXSetColor(highlightColor);
  530.         else if (!COM_Flags.randomBarColors) {
  531.             PSsetgray ((1.0 / (longSize+2.0)) * (index +1));
  532.         } else {
  533.             HSBColor = NXConvertHSBToColor(
  534.                     ((float)index / (float)longSize), 
  535.                 1.0, 1.0);
  536.             NXSetColor(HSBColor);
  537.         }
  538.         [self calcRect:&barRect ofBar:index
  539.             insideRect:(NXRect *)r
  540.             usingMin:minSheetVal];
  541.         NXRectFill(&barRect);
  542.     }
  543.     return self;
  544. }
  545.  
  546.  
  547. /* spacing:    Each plot is a width of 2N and each gap is a width of 1N
  548.  *        total width is m*(2N+N) + N  = 3mN+N = where m = number of bars
  549.  *        Thus N = width / (3m+1)
  550.  *
  551.  * scaling:    The scaling can make some stuff look pretty funny.  
  552.  */
  553. - drawSelf:(NXRect *)r :(int)c
  554. {    
  555.     float    minCellVal, maxCellVal;    /* min & max of values to be plotted */
  556.     float    meanCellVal;        /* mean of the plotted values */
  557.     float    minSheetVal,maxSheetVal;/* RangeOfNumbers will plot in graph */
  558.     NXRect    rectOfPlot;        /* bounds of rect that will hold plot */
  559.     NXSetColor(backgroundColor);
  560.     NXRectFill(&bounds);
  561.     PSsetgray(NX_BLACK);
  562.     switch    (borderType) {
  563.     case    NX_LINE:
  564.             NXFrameRect(&bounds);
  565.             break;
  566.     case    NX_BEZEL:
  567.             NXDrawWhiteBezel(&bounds,&bounds);
  568.             break;
  569.     case    NX_GROOVE:
  570.             NXDrawGroove(&bounds, &bounds);
  571.             break;
  572.     default:    break;
  573.     }
  574.     rectOfPlot= bounds;
  575.     rectOfPlot.origin.x += hMargin;
  576.     rectOfPlot.origin.y += vMargin;
  577.     rectOfPlot.size.width -=2*hMargin;
  578.     rectOfPlot.size.height -=2*vMargin;
  579.     if (        NX_WIDTH(&bounds) < (2*hMargin) || 
  580.             NX_HEIGHT(&bounds) < (2*vMargin) ) 
  581.         return self;
  582.         
  583.     
  584.     [window disableFlushWindow];
  585.     PSgsave();
  586.     PSsetlinewidth(0.0);
  587.     PStranslate (hMargin, vMargin);
  588.     
  589.     if (YES){
  590.     /* great we only have a single axis to work on */
  591.     [self calcMin:&minCellVal andMax:&maxCellVal andMean:&meanCellVal];
  592.  
  593.     if (COM_Flags.autoScale){
  594.         /* give us a plot if they are all the same (but not 0.0)  */
  595.         if (minCellVal == maxCellVal){
  596.                 if (minCellVal > 0.0)    minCellVal = 0.0;
  597.                 else if (maxCellVal < 0.0) maxCellVal = 0.0;
  598.         }
  599.         
  600.         /* figure out what min and the max should be on the sheet */
  601.         if (minCellVal == 0.0)    minSheetVal= 0.0;
  602.         else minSheetVal= minCellVal - ((maxCellVal - minCellVal)*0.2);
  603.         /* we shouldn't have pushed this across the origin */
  604.         if (minSheetVal < 0.0 && minCellVal > 0.0) minSheetVal = 0.0;
  605.         
  606.         if (maxCellVal == 0.0)    maxSheetVal = 0.0;
  607.         else maxSheetVal= maxCellVal +((maxCellVal - minCellVal)* 0.2);
  608.         /* make sure we didn't go across the origin */
  609.         if (maxSheetVal > 0.0 && maxCellVal < 0.0) maxSheetVal = 0.0;
  610.     } else {
  611.         minSheetVal = minSheetSet;
  612.         maxSheetVal = maxSheetSet;
  613.     }
  614.         
  615.     switch(COM_Flags.drawType){
  616.     case    DRAW_H_BAR:
  617.     case    DRAW_V_BAR:
  618.         [self renderBars:&rectOfPlot min:minSheetVal max:maxSheetVal];
  619.         break;
  620.     case    DRAW_H_LINE:
  621.         break;
  622.     case    DRAW_V_LINE:
  623.         [self renderVLines:&rectOfPlot min:minSheetVal max:maxSheetVal ];
  624.         break;
  625.     default:
  626.         break;
  627.     }
  628.  
  629.     PSgrestore();
  630.     if (COM_Flags.drawFrame){
  631.         PSsetgray(NX_BLACK);
  632.         NXFrameRect(&rectOfPlot);
  633.     }
  634.     }
  635.     [window reenableFlushWindow];
  636.     return self;
  637. }
  638.  
  639. @end
  640.